﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using AgFx;
using Coding4Fun.Phone.Controls.Data;
using DepSoft.Mobile.WP7.FollwIt.Framework;
using DepSoft.Mobile.WP7.FollwIt.Messages;
using DepSoft.Mobile.WP7.FollwIt.Model;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using Microsoft.Phone.Controls;
using WP7Contrib.Services.Navigation;
using WP7Contrib.Services.Settings;
using WP7Contrib.Services.Storage;

namespace DepSoft.Mobile.WP7.FollwIt.ViewModel
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm/getstarted
    /// </para>
    /// </summary>
    public class CollectionViewModel : ViewModelBaseWp7
    {
        #region Private members
        enum SelectedItem
        {
            Movie,
            TVShow
        }

        enum PageMode
        {
            First,
            Previous,
            Next
        }

        enum PivotName
        {
            MoviePivotItem,
            TVShowPivotItem
        }

        private agfxMovieCollectionViewModel _movieCollectionVM;
        private agfxTVShowCollectionViewModel _tvShowCollectionVM;

        private int _currentPageMovie = 0;
        private int _currentPageTVShow = 0;
        private int _pageSize = 9; //18;
        private PivotName _currentPivot = PivotName.MoviePivotItem;
        #endregion

        #region Public properties
        public string MoviePageName
        {
            get
            {
                return "movies";
            }
        }

        public string TVPageName
        {
            get
            {
                return "tv shows";
            }
        }

        public RelayCommand RefreshCommand
        {
            get;
            private set;
        }

        public RelayCommand PreviousCommand
        {
            get;
            private set;
        }

        public RelayCommand NextCommand
        {
            get;
            private set;
        }

        public RelayCommand UserProfileCommand
        {
            get;
            private set;
        }

        public RelayCommand AboutCommand
        {
            get;
            private set;
        }

        public RelayCommand<int> MovieItemSelectedCommand
        {
            get;
            private set;
        }

        public RelayCommand<int> TVShowItemSelectedCommand
        {
            get;
            private set;
        }

        public RelayCommand<SelectionChangedEventArgs> SelectPivotCommand
        {
            get;
            private set;
        }
        #endregion

        #region Public bindable properties
        #region MovieCollection
        /// <summary>
        /// The <see cref="MovieCollection" /> property's name.
        /// </summary>
        public const string MovieCollectionPropertyName = "MovieCollection";

        private List<Movie> _movieCollection = null;

        /// <summary>
        /// Gets the MovieCollection property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public List<Movie> MovieCollection
        {
            get
            {
                return _movieCollection;
            }

            set
            {
                this.SetPropertyAndNotify(ref _movieCollection, value, MovieCollectionPropertyName);
            }
        }
        #endregion

        #region TVShowCollection
        /// <summary>
        /// The <see cref="TVShowCollection" /> property's name.
        /// </summary>
        public const string TVShowCollectionPropertyName = "TVShowCollection";

        private List<TVShow> _tvShowCollection = null;

        /// <summary>
        /// Gets the TVShowCollection property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public List<TVShow> TVShowCollection
        {
            get
            {
                return _tvShowCollection;
            }

            set
            {
                this.SetPropertyAndNotify(ref _tvShowCollection, value, TVShowCollectionPropertyName);
            }
        }
        #endregion

        #region ProgressIndicatorVisible
        /// <summary>
        /// The <see cref="ProgressIndicatorVisible" /> property's name.
        /// </summary>
        public const string ProgressIndicatorVisiblePropertyName = "ProgressIndicatorVisible";

        private bool _progressIndicatorVisible = false;

        /// <summary>
        /// Gets the ProgressIndicatorVisible property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public bool ProgressIndicatorVisible
        {
            get
            {
                return _progressIndicatorVisible;
            }

            set
            {
                this.SetPropertyAndNotify(ref _progressIndicatorVisible, value, ProgressIndicatorVisiblePropertyName);
            }
        }
        #endregion

        #region CollectionVisible
        /// <summary>
        /// The <see cref="CollectionVisible" /> property's name.
        /// </summary>
        public const string CollectionVisiblePropertyName = "CollectionVisible";

        private Visibility _collectionVisible = Visibility.Collapsed;

        /// <summary>
        /// Gets the CollectionVisible property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public Visibility CollectionVisible
        {
            get
            {
                return _collectionVisible;
            }

            set
            {
                this.SetPropertyAndNotify(ref _collectionVisible, value, CollectionVisiblePropertyName);
            }
        }
        #endregion

        #region SelectedFriend
        /// <summary>
        /// The <see cref="SelectedFriend" /> property's name.
        /// </summary>
        public const string SelectedFriendPropertyName = "SelectedFriend";

        private string _selectedFriend = string.Empty;

        /// <summary>
        /// Gets the SelectedFriend property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public string SelectedFriend
        {
            get
            {
                return string.IsNullOrEmpty(_selectedFriend) ? FollwItApi.GetInstance().UserName : _selectedFriend;
            }

            set
            {
                this.SetPropertyAndNotify(ref _selectedFriend, value, SelectedFriendPropertyName);
                RaisePropertyChanged(ApplicationTitlePropertyName);
            }
        }
        #endregion

        #region ApplicationTitle
        /// <summary>
        /// The <see cref="ApplicationTitle" /> property's name.
        /// </summary>
        public const string ApplicationTitlePropertyName = "ApplicationTitle";

        private string _applicationTitle = string.Empty;

        /// <summary>
        /// Gets the ApplicationTitle property.
        /// TODO Update documentation:
        /// Changes to that property's value raise the PropertyChanged event. 
        /// This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public string ApplicationTitle
        {
            get
            {
                //Use of the Coding 4 FUn PhoneHelper to get the application title from the configuration
                return string.Concat(PhoneHelper.GetAppAttribute("Title").ToUpper(), " ", this.SelectedFriend.ToUpper());
            }
        }
        #endregion

        #region IsMovieCollectionLoaded
        /// <summary>
        /// The <see cref="IsMovieCollectionLoaded" /> property's name.
        /// </summary>
        public const string IsMovieCollectionLoadedPropertyName = "IsMovieCollectionLoaded";

        private bool _isMovieCollectionLoaded = false;

        /// <summary>
        /// Gets the IsMovieCollectionLoaded property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public bool IsMovieCollectionLoaded
        {
            get
            {
                return _isMovieCollectionLoaded;
            }

            set
            {
                this.SetPropertyAndNotify(ref _isMovieCollectionLoaded, value, IsMovieCollectionLoadedPropertyName);
            }
        }
        #endregion

        #region IsTVShowCollectionLoaded
        /// <summary>
        /// The <see cref="IsTVShowCollectionLoaded" /> property's name.
        /// </summary>
        public const string IsTVShowCollectionLoadedPropertyName = "IsTVShowCollectionLoaded";

        private bool _isTVShowCollectionLoaded = false;

        /// <summary>
        /// Gets the IsTVShowCollectionLoaded property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public bool IsTVShowCollectionLoaded
        {
            get
            {
                return _isTVShowCollectionLoaded;
            }

            set
            {
                this.SetPropertyAndNotify(ref _isTVShowCollectionLoaded, value, IsTVShowCollectionLoadedPropertyName);
            }
        }
        #endregion
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public CollectionViewModel(INavigationService navigationService, IStorageService storageService, IStoreSettings settingsService, IMessenger messenger)
            : base(navigationService, storageService, settingsService, messenger)
        {
            #region Command setup
            this.RefreshCommand = new RelayCommand(() => this.Refresh());
            this.PreviousCommand = new RelayCommand(() => this.Load(false, PageMode.Previous));
            this.NextCommand = new RelayCommand(() => this.Load(false, PageMode.Next));
            this.UserProfileCommand = new RelayCommand(() => navigationService.Navigate(ViewModelLocator.UserProfilePageUri));
            this.MovieItemSelectedCommand = new RelayCommand<int>((movieID) => this.ItemSelected(movieID, SelectedItem.Movie));
            this.TVShowItemSelectedCommand = new RelayCommand<int>((tvShowID) => this.ItemSelected(tvShowID, SelectedItem.TVShow));
            this.SelectPivotCommand = new RelayCommand<SelectionChangedEventArgs>((args) => _currentPivot = Enum<PivotName>.Parse(((PivotItem)args.AddedItems[0]).Name));
            this.AboutCommand = new RelayCommand(() => NavigationService.Navigate(new Uri("/YourLastAboutDialog;component/AboutPage.xaml", UriKind.Relative))); 
            #endregion

            this.MessengerInstance.Register<SelectedFriendChangedMessage>(this, selectedFriend =>
            {
                this.SelectedFriend = selectedFriend.UserName;
                this.Load(true);
            });

            //Look for property changes, so we can correctly adjust the ProgressIndicator
            this.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(MainViewModel_PropertyChanged);

            this.Load(true);
        }
        #endregion

        #region Private methods
        private void MainViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == IsMovieCollectionLoadedPropertyName || e.PropertyName == IsTVShowCollectionLoadedPropertyName)
            {
                //If all collections are loaded we disable the progress indicator
                if (this.IsMovieCollectionLoaded && this.IsTVShowCollectionLoaded)
                    this.ProgressIndicatorVisible = false;
            }
        }

        private void Load(bool triggerCollectionVisibility, PageMode pageMode = PageMode.First)
        {
            if (triggerCollectionVisibility)
                this.CollectionVisible = Visibility.Collapsed;

            this.ProgressIndicatorVisible = true;
            this.IsMovieCollectionLoaded = false;
            this.IsTVShowCollectionLoaded = false;

            DataManager.Current.Load<agfxMovieCollectionViewModel>(this.SelectedFriend, (vm) =>
                {
                    //this.MovieCollection = vm;
                    _movieCollectionVM = vm;

                    switch (pageMode)
                    {
                        case PageMode.First:
                            this.MovieCollection = (from movie in _movieCollectionVM.Movies
                                                    select movie).Take(_pageSize).ToList();
                            _currentPageMovie = 0;
                            break;
                        case PageMode.Previous:
                            if (_currentPivot == PivotName.MoviePivotItem)
                            {
                                if (_currentPageMovie == 0)
                                    this.MovieCollection = (from movie in _movieCollectionVM.Movies
                                                            select movie).Take(_pageSize).ToList();
                                else
                                {
                                    _currentPageMovie = --_currentPageMovie;
                                    this.MovieCollection = (from movie in _movieCollectionVM.Movies
                                                            select movie).Skip(_pageSize * _currentPageMovie).Take(_pageSize).ToList();
                                }
                            }
                            break;
                        case PageMode.Next:
                            if (_currentPivot == PivotName.MoviePivotItem)
                            {
                                _currentPageMovie = ++_currentPageMovie;
                                if ((_pageSize * _currentPageMovie) > _movieCollectionVM.Movies.Count)
                                    _currentPageMovie = --_currentPageMovie;
                                else
                                    this.MovieCollection = (from movie in _movieCollectionVM.Movies
                                                            select movie).Skip(_pageSize * _currentPageMovie).Take(_pageSize).ToList();
                            }
                            break;
                        default:
                            break;
                    }

                    this.IsMovieCollectionLoaded = true;
                    if (triggerCollectionVisibility)
                        this.CollectionVisible = Visibility.Visible;
                },
                (ex) =>
                {
                    this.IsMovieCollectionLoaded = true;
                    throw new FollwitException("Error while loading Movie Collection", ex);
                });

            DataManager.Current.Load<agfxTVShowCollectionViewModel>(this.SelectedFriend, (vm) =>
                {
                    //this.TVShowCollection = vm;
                    _tvShowCollectionVM = vm;
                    switch (pageMode)
                    {
                        case PageMode.First:
                            this.TVShowCollection = (from tvShow in _tvShowCollectionVM.TVShows
                                                     select tvShow).Take(_pageSize).ToList();
                            _currentPageTVShow = 0;
                            break;
                        case PageMode.Previous:
                            if (_currentPivot == PivotName.TVShowPivotItem)
                            {
                                if (_currentPageTVShow == 0)
                                    this.TVShowCollection = (from tvShow in _tvShowCollectionVM.TVShows
                                                             select tvShow).Take(_pageSize).ToList();
                                else
                                {
                                    _currentPageTVShow = --_currentPageTVShow;
                                    this.TVShowCollection = (from tvShow in _tvShowCollectionVM.TVShows
                                                             select tvShow).Skip(_pageSize * _currentPageTVShow).Take(_pageSize).ToList();
                                }
                            }
                            break;
                        case PageMode.Next:
                            if (_currentPivot == PivotName.TVShowPivotItem)
                            {
                                _currentPageTVShow = ++_currentPageTVShow;
                                if ((_pageSize * _currentPageTVShow) > _tvShowCollectionVM.TVShows.Count)
                                    _currentPageTVShow = --_currentPageTVShow;
                                else
                                    this.TVShowCollection = (from tvShow in _tvShowCollectionVM.TVShows
                                                             select tvShow).Skip(_pageSize * _currentPageTVShow).Take(_pageSize).ToList();
                            }
                            break;
                        default:
                            break;
                    }

                    this.IsTVShowCollectionLoaded = true;
                    if (triggerCollectionVisibility)
                        this.CollectionVisible = Visibility.Visible;
                },
                (ex) =>
                {
                    this.IsTVShowCollectionLoaded = true;
                    throw new FollwitException("Error while loading TVShow Collection", ex);
                });
        }

        private void Refresh()
        {
            this.ProgressIndicatorVisible = true;
            this.IsMovieCollectionLoaded = false;
            this.IsTVShowCollectionLoaded = false;

            DataManager.Current.Refresh<agfxMovieCollectionViewModel>(this.SelectedFriend, (vm) =>
            {
                _movieCollectionVM = vm;
                this.MovieCollection = (from movie in _movieCollectionVM.Movies
                                        select movie).Take(_pageSize).ToList();
                _currentPageMovie = 0;
                this.IsMovieCollectionLoaded = true;
            },
                (ex) =>
                {
                    this.IsMovieCollectionLoaded = true;
                    throw new FollwitException("Error while refreshing Movie Collection", ex);
                });

            DataManager.Current.Refresh<agfxTVShowCollectionViewModel>(this.SelectedFriend, (vm) =>
            {
                _tvShowCollectionVM = vm;
                this.TVShowCollection = (from tvShow in _tvShowCollectionVM.TVShows
                                         select tvShow).Take(_pageSize).ToList();
                _currentPageTVShow = 0;
                this.IsTVShowCollectionLoaded = true;
            },
                (ex) =>
                {
                    this.IsTVShowCollectionLoaded = true;
                    throw new FollwitException("Error while refreshing TVShow Collection", ex);
                });
        }

        private void ItemSelected(int itemID, SelectedItem type)
        {
            switch (type)
            {
                case SelectedItem.Movie:
                    NavigationService.Navigate(ViewModelLocator.MovieSummaryPageUri);
                    this.MessengerInstance.Send<SelectedMovieChangedMessage>(new SelectedMovieChangedMessage(itemID));
                    break;
                case SelectedItem.TVShow:
                    NavigationService.Navigate(ViewModelLocator.TVShowSummaryPageUri);
                    this.MessengerInstance.Send<SelectedTVShowChangedMessage>(new SelectedTVShowChangedMessage(itemID));
                    break;
                default:
                    break;
            }
        }
        #endregion

        #region ViewModelBaseWp7 implementation
        protected override void IsBeingActivated(WP7Contrib.Services.Storage.IStorage storage)
        {
            this.MessengerInstance.Send<SelectedFriendChangedMessage>(new SelectedFriendChangedMessage(storage.Read<string>("SelectedFriend")));
        }

        protected override void IsBeingDeactivated(WP7Contrib.Services.Storage.IStorage storage)
        {
            storage.Write<string>("SelectedFriend", _selectedFriend);
        }
        #endregion
    }
}